﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using LiSAClient.Core;
using LiSAClient.Core.Messages;
using LiSAClient.Core.Models;
using LiSAClient.Core.Services;
using LiSAClient.Core.Services.Factories;
using LiSAClient.Core.Viewmodel;
using LiSAClient.LiSAService;
using LiSAClient.WpfClient.ViewModel.Projects.ArticleFeedback;

namespace LiSAClient.WpfClient.ViewModel.Projects
{
    public class WorkOnProjectViewModel : ContentViewModel
    {
        #region Observables
        private readonly Guid projectID;

        private List<LiSAService.ArticleFeedback> AllFeedbackInDatabase { get; set; }

        public ICommand Commit { get; private set; }

        public ICommand UpdateSelectedItem { get; private set; }

        public ICommand SelectArticle { get; private set; }

        public ICommand RemoveDuplicatesCmd { get; private set; }

        public string ArticleType
        {
            get { if (this.selectedArticle == null) return string.Empty;
                return this.selectedArticle.Type ?? string.Empty; }

            set
            {
                this.selectedArticle.Type = value;
                this.UpdateSelectedArticle();
            }
        }

        private string lead;

        public string Lead
        {
            get { return this.lead; }

            set { this.Set(() => this.Lead, ref this.lead, value); }
        }

        private List<string> articleTypes;

        public List<string> ArticleTypes
        {
            get { return this.articleTypes; }

            set { this.Set(() => this.ArticleTypes, ref this.articleTypes, value); }
        }

        private List<FeedbackStageViewModel> feedbackStages;

        public List<FeedbackStageViewModel> FeedbackStages
        {
            get { return this.feedbackStages; }

            set { this.Set(() => this.FeedbackStages, ref this.feedbackStages, value); }
        }

        private string selectedArticleDate;

        public string SelectedArticleDate
        {
            get { return this.selectedArticleDate; }

            set { this.Set(() => this.SelectedArticleDate, ref this.selectedArticleDate, value); }
        }

        private Project project;

        public Project Project
        {
            get { return this.project; }

            set { this.Set(() => this.Project, ref this.project, value); }
        }

        private Article selectedArticle;

        public Article SelectedArticle
        {
            get { return this.selectedArticle; }

            set
            {
                if (this.SelectedArticle != null && this.SelectedArticle.Type == "ARTICLE") this.SelectedArticle.Type = string.Empty;
                this.Set(() => this.SelectedArticle, ref this.selectedArticle, value);
                // Update currently selected article if there is one before switching out
                if (this.selectedArticle != null && this.selectedArticle.ID != Guid.Empty)
                {
                    UpdateSelectedArticle();
                }
                this.SelectedArticleDate = this.SelectedArticle.PublicationDate;
                this.PrepareSelectedArticleFeedback();
            }
        }
        #endregion

        #region Initializers

        /// <summary>
        /// Initiate the viewmodel
        /// </summary>
        /// <param name="projectID">ID of the project to work on</param>
        public WorkOnProjectViewModel(Guid projectID)
        {
            this.FeedbackStages = new List<FeedbackStageViewModel>();
            this.projectID = projectID;
            this.Project = new Project();
            this.Commit = new RelayCommand(SaveProjectWork);
            this.SelectArticle = new RelayCommand(DoSelectArticle);
            this.RemoveDuplicatesCmd = new RelayCommand(DoRemoveDuplicates);
            this.UpdateSelectedItem = new RelayCommand(UpdateSelectedArticle);
            this.ArticleTypes = new List<string>
            {
                string.Empty,
                "Article",
                "Journal",
                "Conference",
                "Book",
                "Workshop",
                "Misc"
            };
            Messenger.Default.Register<ArticleFeedbackChangedMessage>(this, this.HandleArticleChanged);
        }

        /// <summary>
        /// Load the project and set it
        /// </summary>
        public override void OnPresented()
        {
            this.Presenter.StartWaiting("Fetching project information...");
            Task.Factory.StartNew(
                () =>
                {
                    try
                    {
                        var theProject = ServiceFactory.Instance.Get<ProjectService>().GetProjectByID(projectID);
                        if (theProject == null || theProject.ProjectID == Guid.Empty)
                        {
                            Messenger.Default.Send(new ShowNotificationMessage
                            {
                                NotificationType = NotificationType.Error,
                                Header = "Error!",
                                Body = "The project was not found."
                            });
                            this.Presenter.FinishedWaiting();
                            return;
                        }
                        AllFeedbackInDatabase = ServiceFactory.Instance.Get<ProjectService>()
                            .GetArticleFeedback(projectID, AppContext.CurrentContext.User.ID);
                        this.Project = theProject;
                        var singleOrDefault = Project.Users.SingleOrDefault(x => x.ID == Project.ProjectLead);
                        this.Lead = singleOrDefault != null ? singleOrDefault.FullName : "Unknown";

                        this.Presenter.FinishedWaiting();
                    }
                    catch (Exception exc)
                    {
                        Messenger.Default.Send(new ShowNotificationMessage
                        {
                            NotificationType = NotificationType.Error,
                            Header = "Error!",
                            Body = exc.Message
                        });
                        this.Presenter.FinishedWaiting();
                    }
                });
        }

        #endregion

        #region methods

        /// <summary>
        /// Updates the selectedArticle
        /// </summary>
        private void UpdateSelectedArticle()
        {
            ServiceFactory.Instance.Get<ProjectService>().UpdateArticle(this.SelectedArticle);
        }

        /// <summary>
        /// Removes all articles marked as "Duplicate" and notifies the user when done
        /// </summary>
        public void DoRemoveDuplicates()
        {
            var projectArticles = new List<Article>();
            projectArticles.AddRange(this.Project.Articles);
            var remainingArticles = new List<Article>();
            foreach (var article in this.Project.Articles)
            {
                if (article.IsDuplicate)
                {
                    this.Project.RemovedArticles.Add(article.ID);
                    var removedArticle = projectArticles.SingleOrDefault(x => x.ID == article.ID);
                    projectArticles.Remove(removedArticle);
                }
                else
                {
                    remainingArticles.Add(article);
                }
            }
            this.Project.Articles = remainingArticles;
            // Update in the database
            ServiceFactory.Instance.Get<ProjectService>().UpdateProject(this.Project);
            // notify user
            Messenger.Default.Send(new ShowNotificationMessage
            {
                NotificationType = NotificationType.Info,
                Header = "Duplicates removed",
                Body = "Marked duplicates have been removed."
            });
        }

        /// <summary>
        /// TODO: Popup with more article information, full article text etc.
        /// </summary>
        public void DoSelectArticle()
        {
            return;
        }

        /// <summary>
        /// Save the project work and close the window
        /// </summary>
        public void SaveProjectWork()
        {
            // update the currently selected article
            UpdateSelectedArticle();
            // Ensure legality
            if (string.IsNullOrWhiteSpace(this.Project.Title))
            {
                Messenger.Default.Send(new ShowNotificationMessage
                {
                    NotificationType = NotificationType.Warning,
                    Header = "Warning!",
                    Body = "The project must have a title."
                });
                return;
            }
            if (string.IsNullOrWhiteSpace(this.Project.Description))
            {
                Messenger.Default.Send(new ShowNotificationMessage
                {
                    NotificationType = NotificationType.Warning,
                    Header = "Warning!",
                    Body = "The project must have a description."
                });
                return;
            }

            this.Project.ProjectLead = AppContext.CurrentContext.User.ID;

            ServiceFactory.Instance.Get<ProjectService>().UpdateProject(this.Project);

            // Close the window
            if (this.Presenter != null)
            {
                this.Presenter.SendClosingRequest();
            }
        }

        /// <summary>
        /// When a selected article is, well, selected, prepare the viewmodel for the feedback
        /// </summary>
        public void PrepareSelectedArticleFeedback()
        {
            var articleFeedback = AllFeedbackInDatabase.Where(x => x.ArticleID == this.SelectedArticle.ID).ToList();
            var stages = new List<FeedbackStageViewModel>();
            foreach (var projectStage in this.Project.Stages)
            {
                var potFeedbackExist = new LiSAService.ArticleFeedback();
                if (articleFeedback.Any(x => (x.Stage == projectStage.Stage) && (x.ArticleID == SelectedArticle.ID)))
                {
                    // Security against previous bug
                    if (articleFeedback.Any(x => (x.Stage == projectStage.Stage) && (x.ArticleID == SelectedArticle.ID)))
                    {
                        var keep = true;
                        var removallist = new List<LiSAService.ArticleFeedback>();
                        foreach (var theFeedback in articleFeedback.Where(x => (x.Stage == projectStage.Stage) && (x.ArticleID == SelectedArticle.ID)))
                        {
                            if (!keep)
                            {
                                removallist.Add(theFeedback);
                            }
                            keep = false;
                        }
                        foreach (var feedback in removallist)
                        {
                            articleFeedback.Remove(feedback);
                        }
                    }
                    potFeedbackExist = articleFeedback.SingleOrDefault(x => (x.Stage == projectStage.Stage) && (x.ArticleID == SelectedArticle.ID));
                }
                else
                {
                    potFeedbackExist = new LiSAService.ArticleFeedback
                    {
                        ID = Guid.NewGuid(),
                        ArticleID = SelectedArticle.ID,
                        ProjectID = Project.ProjectID,
                        Stage = projectStage.Stage,
                        ReviewerUserID = AppContext.CurrentContext.User.ID,
                        Comment = "AutoGenerated"
                    };
                }
                stages.Add(new FeedbackStageViewModel(projectStage, potFeedbackExist));
            }
            FeedbackStages = stages.OrderBy(x => x.Stage.Stage).ToList();
        }

        /// <summary>
        /// Handles updating the in-memory project when the child stages are changed
        /// </summary>
        /// <param name="msg">The ArticleFeedback that has changed</param>
        public void HandleArticleChanged(ArticleFeedbackChangedMessage msg)
        {
            var editedFeedbackStage = new FeedbackStageViewModel(msg.Stage, msg.ChangedFeedback);
            if (this.FeedbackStages.Any(x => x.TheFeedback.ID == msg.ChangedFeedback.ID))
            {
                this.FeedbackStages.Remove(
                    this.FeedbackStages.SingleOrDefault(x => x.TheFeedback.ID == msg.ChangedFeedback.ID));
            }
            this.FeedbackStages.Add(editedFeedbackStage);

            //Update the ArticleFeedback in memory
            try
            {
                var existingfeedback =
                    AllFeedbackInDatabase.Single(
                        x => x.ArticleID == this.SelectedArticle.ID && x.Stage == msg.Stage.Stage);
                if (existingfeedback != null)
                {
                    existingfeedback.OptionSelected = msg.ChangedFeedback.OptionSelected;
                }
                else
                {
                    AllFeedbackInDatabase.Add(new LiSAService.ArticleFeedback
                    {
                        ID = Guid.NewGuid(),
                        ArticleID = SelectedArticle.ID,
                        ProjectID = Project.ProjectID,
                        Stage = msg.Stage.Stage,
                        ReviewerUserID = AppContext.CurrentContext.User.ID,
                        Comment = "AutoGenerated"
                    });
                }
            }
            catch
            {
                AllFeedbackInDatabase.Add(new LiSAService.ArticleFeedback
                {
                    ID = Guid.NewGuid(),
                    ArticleID = SelectedArticle.ID,
                    ProjectID = Project.ProjectID,
                    Stage = msg.Stage.Stage,
                    ReviewerUserID = AppContext.CurrentContext.User.ID,
                    Comment = "AutoGenerated",
                    OptionSelected = msg.ChangedFeedback.OptionSelected
                });
            }
        }

        #endregion
    }
}
